﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Tycho.Peyote {
    /// <summary>
    /// Interaction logic for PlayBox.xaml
    /// </summary>
    public partial class GlyphSpace : UserControl, IGlyphLayout {
        private int Rows, Columns;
        private Table<PlayBox> PlayBoxTable;
        private Dictionary<GlyphHandle, PlayBox> GlyphPlayBoxes;
        public Environment Environment { get; set; }

        public GlyphSpace () {
            InitializeComponent ();

            Rows = 0;
            Columns = 0;
            PlayBoxTable = new Table<PlayBox> ();
            GlyphPlayBoxes = new Dictionary<GlyphHandle, PlayBox> ();
        }

        public void AddRow () {
            var row = new RowDefinition ();
            row.Height = new GridLength (0.0, GridUnitType.Auto);
            row.MinHeight = 120;
            Space.RowDefinitions.Add (row);

            for (int i = 0; i < Columns; i++) {
                PlacePlayBox (Rows, i);
            }

            Rows++;
        }

        public void AddColumn () {
            var column = new ColumnDefinition ();
            column.Width = new GridLength(0.0, GridUnitType.Auto);
            column.MinWidth = 120;
            Space.ColumnDefinitions.Add (column);

            for (int i = 0; i < Rows; i++) {
                PlacePlayBox (i, Columns);
            }

            Columns++;
        }

        private void PlacePlayBox (int row, int column) {
            PlayBox box = new PlayBox (column, row, Environment);
            box.GlyphMoved += PlayBox_GlyphMoved;
            Grid.SetRow (box, row);
            Grid.SetColumn (box, column);

            PlayBoxTable [row, column] = box;

            Space.Children.Add (box);
        }

        void PlayBox_GlyphMoved (object sender, GlyphMovementEvent e) {
            var playBox = (PlayBox) sender;

            if (!playBox.IsEmpty) {
                if (playBox.Column >= PlayBoxTable.Width - 1) {
                    AddColumn ();
                }
                if (playBox.Row >= PlayBoxTable.Height - 1) {
                    AddRow ();
                }
            }
        }

        #region IGlyphLayout Members

        public void PlaceGlyph (GlyphHandle glyphHandle, GlyphHandle nextTo) {
            PlayBox playBox;
            if (GlyphPlayBoxes.TryGetValue (nextTo, out playBox)) {
                playBox.PlaceGlyph (glyphHandle, nextTo);
                GlyphPlayBoxes [glyphHandle] = playBox;
            } else {
                throw new ApplicationException ("Could not find glyph: " + nextTo);
            }
        }

        public void LinkGlyphs (GlyphHandle from, GlyphHandle to) {
            throw new NotImplementedException ();
        }

        public void RemoveGlyph (GlyphHandle glyphHandle) {
            PlayBox playBox;
            if (GlyphPlayBoxes.TryGetValue (glyphHandle, out playBox)) {
                playBox.RemoveGlyph (glyphHandle);
            }
        }

        public void MoveGlyph (GlyphHandle glyphHandle, PlayBox toPlayBox) {
            PlayBox fromPlayBox;

            if (GlyphPlayBoxes.TryGetValue (glyphHandle, out fromPlayBox)) {
                fromPlayBox.RemoveGlyph (glyphHandle);
            }

            toPlayBox.AddGlyph (glyphHandle);
            GlyphPlayBoxes [glyphHandle] = toPlayBox;
        }

        #endregion

        public void PlaceGlyphInBox (GlyphHandle glyphHandle, int row, int column) {
            PlayBox playBox = PlayBoxTable [row, column];
            playBox.AddGlyph (glyphHandle);
            GlyphPlayBoxes [glyphHandle] = playBox;
        }

        class Table<T> where T : class {
            List<List<T>> Rows;
            public int Height { get; private set; }
            public int Width { get; private set; }

            public Table () {
                Rows = new List<List<T>> ();
            }

            public T this [int row, int column] {
                get {
                    return Rows [row] [column];
                }
                set {
                    while (row > Rows.Count) {
                        Rows.Add (new List<T> ());
                    }

                    List<T> rowList = new List<T> ();
                    Rows.Add (rowList);

                    while (column > rowList.Count) {
                        rowList.Add (default (T));
                    }

                    rowList.Add (value);

                    Width = Math.Max (Width, column + 1);
                    Height = Math.Max (Height, row + 1);
                }
            }

            public void RemoveRow (int row) {
                Rows.RemoveAt (row);
            }

            public void RemoveColumn (int column) {
                foreach (var cells in Rows) {
                    if (column < cells.Count) {
                        cells.RemoveAt (column);
                    }
                }
            }

            public IEnumerable<T> GetRow (int row) {
                return Rows[row];
            }

            public IEnumerable<T> GetColumn (int column) {
                foreach (var cells in Rows) {
                    if (column < cells.Count) {
                        yield return cells[column];
                    } else {
                        yield return default (T);
                    }
                }
            }
        }
    }
}
